Chapter 10: Tibbles

Read R4ds Chapter 10: Tibbles, sections 1-3.

10.1: Introduction

Load the tidyverse package.

library(tidyverse)
-- Attaching packages --------------------------------------- tidyverse 1.2.1 --
v ggplot2 3.1.0     v purrr   0.2.5
v tibble  1.4.2     v dplyr   0.7.8
v tidyr   0.8.2     v stringr 1.3.1
v readr   1.3.1     v forcats 0.3.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

10.2: Creating tibbles

Enter your code chunks for Section 10.2 here.

tibble(
  x = 1:5,
  y = 1,
  z = x ^ 2 + y
)
tb <- tibble(
  `:)` = "smile",
  ` ` = "space",
  `2000` = "number"
)
tb
tribble(
  ~x, ~y, ~z,
  #--|--|----
  "a", 2, 3.6,
  "b", 1, 8.5
)

Describe what each chunk code does.

1: squares x and adds y 2: refers to non-syntactic names 3: columnized date entry that is customised and layed out to be easier to read

10.3: Tibbles vs data.frame

Enter your code chunks for Section 10.2 here.

tibble(
  a = lubridate::now() + runif(1e3) * 86400,
  b = lubridate::today() + runif(1e3) * 30,
  c = 1:1e3,
  d = runif(1e3),
  e = sample(letters, 1e3, replace = TRUE)
)
nycflights13::flights %>% 
  print(n = 10, width = Inf)
nycflights13::flights %>% 
  View()
df <- tibble(
  x = runif(5),
  y = rnorm(5)
)
df$x
[1] 0.4134812 0.6645216 0.2313980 0.3303780
[5] 0.1114870
df[["x"]]
[1] 0.4134812 0.6645216 0.2313980 0.3303780
[5] 0.1114870
df[[1]]
[1] 0.4134812 0.6645216 0.2313980 0.3303780
[5] 0.1114870
df %>%  .$x
[1] 0.4134812 0.6645216 0.2313980 0.3303780
[5] 0.1114870
df %>%  .[["x"]]
[1] 0.4134812 0.6645216 0.2313980 0.3303780
[5] 0.1114870

Describe what each chunk code does. 1:shows first 10 rows and all columns that fit on screen, easier to work with large data frames 2:can print a specific data frame 3:scrollable view of the data frame 4:pulls out a single vairable 5:to use variables in a pipe

10.4: Not required

Section 10.5 Questions

Answer the questions completely. Use code chunks, text, or both, as necessary.

1: How can you tell if an object is a tibble? (Hint: try printing mtcars, which is a regular data frame). Identify at least two ways to tell if an object is a tibble. Hint: What does as_tibble() do? What does class() do? What does str() do?

mtcars
as_tibble(mtcars)
is_tibble(mtcars)
[1] FALSE
class(mtcars)
[1] "data.frame"
str(mtcars)
'data.frame':   32 obs. of  11 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...

Using is_tibble for true or false as_tibble prints off the first ten observations class is used to find the class of an object str gives a summary of the table

2: Compare and contrast the following operations on a data.frame and equivalent tibble. What is different? Why might the default data frame behaviours cause you frustration?

df <- data.frame(abc = 1, xyz = "a")
df$x
df[, "xyz"]
df[, c("abc", "xyz")]
tbl <- as_tibble(df)
tbl$xyz
[1] a
Levels: a
tbl[, "xyz"]
tbl[, c("abc", "xyz")]

the difference is if the data frames have variables then it will cause a problem in the code

Chapter 11: Importing data

Read R4ds Chapter 11: Data Import, sections 1, 2, and 5.

11.1 Introduction

Nothing to do here unless you took a break and need to reload tidyverse.

11.2 Getting started.

Do not run the first code chunk of this section, which begins with heights <- read_csv("data/heights.csv"). You do not have that data file so the code will not run.

Enter and run the remaining chunks in this section.

read_csv("a,b,c
         1,2,3
         4,5,6")
read_csv("The first line of metadata
         The second line of metadata
         x,y,z
         1,2,3", skip = 2)
read_csv("# A comment I want to skip
         x,y,z
         1,2,3", comment = "#")
read_csv("1,2,3\n4,5,6", col_names = FALSE)
read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))
read_csv("a,b,c\n1,2,.", na = ".")

11.2 Questions

1: What function would you use to read a file where fields were separated with “|”?

read_delim( , delim = “|”)

2: (This question is modified from the text.) Finish the two lines of read_delim code so that the first one would read a comma-separated file and the second would read a tab-separated file. You only need to worry about the delimiter. Do not worry about other arguments. Replace the dots in each line with the rest of your code.

Comma-separated

file <- read_delim("file_csv", "1,2,3")

Tab-separated

file <- read_delim("file_tsv", "1,2,3")

3: What are the two most important arguments to read_fwf()? Why? fwf_positions and fwf_widths specifiy by positions or widths of fixed width files

4: Skip this question

5: Identify what is wrong with each of the following inline CSV files. What happens when you run the code?

read_csv("a,b\n1,2,3\n4,5,6")
2 parsing failures.
row col  expected    actual         file
  1  -- 2 columns 3 columns literal data
  2  -- 2 columns 3 columns literal data
read_csv("a,b,c\n1,2\n1,2,3,4")
2 parsing failures.
row col  expected    actual         file
  1  -- 3 columns 2 columns literal data
  2  -- 3 columns 4 columns literal data
read_csv("a,b\n\"1")
2 parsing failures.
row col                     expected    actual         file
  1  a  closing quote at end of file           literal data
  1  -- 2 columns                    1 columns literal data
read_csv("a,b\n1,2\na,b")
read_csv("a;b\n1;3")

1: number of columns doesnt match header 2: number of columns doesnt match header 3: column b is NA 4: a b are in the values 5: semicolon used instead of comma

11.3 and 11.4: Not required

11.5: Writing to a file

Just read this section. You may find it helpful in the future to save a data file to your hard drive. It is basically the same format as reading a file, except that you must specify the data object to save, in addition to the path and file name.

11.6 Not required

Chapter 18: Pipes

Read R4ds Chapter 18: Pipes, sections 1-3.

Nothing to do otherwise for this chapter. Is this easy or what?

Note: Trying using pipes for all of the remaining examples. That will help you understand them.

Chapter 12: Tidy Data

Read R4ds Chapter 12: Tidy Data, sections 1-3, 7.

12.1 Introduction

Nothing to do here unless you took a break and need to reload the tidyverse.

12.2 Tidy data

Study Figure 12.1 and relate the diagram to the three rules listed just above them. Relate that back to the example I gave you in the notes. Bear this in mind as you make data tidy in the second part of this assignment.

You do not have to run any of the examples in this section.

12.3

Read and run the examples through section 12.3.1 (gathering), including the example with left_join(). We’ll cover joins later.

table4a
tidy4a <- table4a %>% 
  gather(`1999`,`2000`, key = "year", value = "cases")
tidy4b <- table4b %>% 
  gather(`1999`,`2000`, key = "year", value = population)
left_join(tidy4a, tidy4b)
Joining, by = c("country", "year")

12.3 Questions

2: Why does this code fail? Fix it so it works.

table4a %>% 
  gather(`1999`, `2000`, key = "year", value = "cases")
#> Error in inds_combine(.vars, ind_list): Position must be between 0 and n

the years did not have back tickmarks on them `

That is all for Chapter 12. On to the last chapter.

Chapter 5: Data transformation

Read R4ds Chapter 5: Data Transformation, sections 1-4.

Time to get small.

5.1: Introduction

Load the necessary libraries. As usual, type the examples into and run the code chunks.

nycflights13::flights

5.2: Filter rows with filter()

Study Figure 5.1 carefully. Once you learn the &, |, and ! logic, you will find them to be very powerful tools.

5.2 Questions

1.1: Find all flights with a delay of 2 hours or more.

nycflights13::flights
flights <- nycflights13::flights
filter(flights , arr_delay >= 120)

1.2: Flew to Houston (IAH or HOU)

filter(flights, dest == "IAH" | dest == "HOU")

1.3: Were operated by United (UA), American (AA), or Delta (DL).

nycflights13::airlines
filter(flights, carrier %in% c("AA", "DL", "UA"))

1.4: Departed in summer (July, August, and September).

filter(flights, month >= 7, month <= 9)

1.5: Arrived more than two hours late, but didn’t leave late.

filter(flights, dep_delay <= 0, arr_delay > 120)

1.6: Were delayed by at least an hour, but made up over 30 minutes in flight. This is a tricky one. Do your best.

filter(flights, dep_delay >= 60, dep_delay - arr_delay > 30)

1.7: Departed between midnight and 6am (inclusive)

filter(flights, dep_time <= 600 | dep_time == 2400)

2: Another useful dplyr filtering helper is between(). What does it do? Can you use it to simplify the code needed to answer the previous challenges?

finds the months inbetween 7&9 the summer months

filter(flights, between(month, 7, 9))

3: How many flights have a missing dep_time? What other variables are missing? What might these rows represent?

filter(flights, is.na(dep_time))

arrival time, departure delay, arrival delay canceled flights

4: Why is NA ^ 0 not missing? Why is NA | TRUE not missing? Why is FALSE & NA not missing? Can you figure out the general rule? (NA * 0 is a tricky counterexample!)

NA ^ 0
[1] 1
NA | TRUE
[1] TRUE
NA & FALSE
[1] FALSE

NA ^ 0 is not = 0 Note: For some context, see this thread

5.3 Arrange with arrange()

5.3 Questions

1: How could you use arrange() to sort all missing values to the start? (Hint: use is.na()). Note: This one should still have the earliest departure dates after the NAs. Hint: What does desc() do? places it in ascending order

arrange(flights, desc(is.na(dep_time)), dep_time)

2: Sort flights to find the most delayed flights. Find the flights that left earliest.

This question is asking for the flights that were most delayed (left latest after scheduled departure time) and least delayed (left ahead of scheduled time).

most delay: jan 9 2013 at 9:00 earliest dep: dec 7 2013 at 21:23

arrange(flights, desc(dep_delay))
arrange(flights, dep_delay)

3: Sort flights to find the fastest flights. Interpret fastest to mean shortest time in the air.

arrange(flights, air_time) 

Optional challenge: fastest flight could refer to fastest air speed. Speed is measured in miles per hour but time is minutes. Arrange the data by fastest air speed.

arrange(flights, distance / air_time * 60)

4: Which flights travelled the longest? Which travelled the shortest?

Longest:4983 Shortet:17

arrange(flights, desc(distance))
arrange(flights, distance)

5.4 Select columns with select()

5.4 Questions

1: Brainstorm as many ways as possible to select dep_time, dep_delay, arr_time, and arr_delay from flights. Find at least three ways.

select(flights, dep_time, dep_delay, arr_time, arr_delay)
select(flights, 4, 6, 7, 9)
select(flights, "dep_time", "dep_delay", "arr_time", "arr_delay")

2: What happens if you include the name of a variable multiple times in a select() call?

If you repeat variables they get ignored

3: What does the one_of() function do? Why might it be helpful in conjunction with this vector?

vars <- c(“year”, “month”, “day”, “dep_delay”, “arr_delay”)`

it is easier to use vectors than “”

4: Does the result of running the following code surprise you? How do the select helpers deal with case by default? How can you change that default?

select(flights, contains(“TIME”))

select(flights, contains("TIME"))
select(flights, contains("Time", ignore.case = FALSE))
LS0tDQp0aXRsZTogIkhXMDUgUGFydCAxOiBDb21wbGV0ZSB0aGUgc2VjdGlvbnMiDQphdXRob3I6ICJNZWx2aW4gQ3VtbWluZ3MiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KLSBDaGFuZ2UgInlvdXIgbmFtZSIgaW4gdGhlIFlBTUwgaGVhZGVyIGFib3ZlIHRvIHlvdXIgbmFtZS4NCg0KLSBBcyB1c3VhbCwgZW50ZXIgdGhlIGV4YW1wbGVzIGluIGNvZGUgY2h1bmtzIGFuZCBydW4gdGhlbSwgdW5sZXNzIHRvbGQgb3RoZXJ3aXNlLg0KDQojIyBDaGFwdGVyIDEwOiBUaWJibGVzDQoNClJlYWQgW1I0ZHMgQ2hhcHRlciAxMDogVGliYmxlc10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWJibGVzLmh0bWwpLCBzZWN0aW9ucyAxLTMuDQoNCiMjIyAxMC4xOiBJbnRyb2R1Y3Rpb24NCg0KTG9hZCB0aGUgdGlkeXZlcnNlIHBhY2thZ2UuIA0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCiMjIyAxMC4yOiBDcmVhdGluZyB0aWJibGVzDQoNCkVudGVyIHlvdXIgY29kZSBjaHVua3MgZm9yIFNlY3Rpb24gMTAuMiBoZXJlLg0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6NSwNCiAgeSA9IDEsDQogIHogPSB4IF4gMiArIHkNCikNCmBgYA0KDQpgYGB7cn0NCnRiIDwtIHRpYmJsZSgNCiAgYDopYCA9ICJzbWlsZSIsDQogIGAgYCA9ICJzcGFjZSIsDQogIGAyMDAwYCA9ICJudW1iZXIiDQopDQp0Yg0KYGBgDQoNCmBgYHtyfQ0KDQp0cmliYmxlKA0KICB+eCwgfnksIH56LA0KICAjLS18LS18LS0tLQ0KICAiYSIsIDIsIDMuNiwNCiAgImIiLCAxLCA4LjUNCikNCmBgYA0KDQpEZXNjcmliZSB3aGF0IGVhY2ggY2h1bmsgY29kZSBkb2VzLiANCg0KMTogc3F1YXJlcyB4IGFuZCBhZGRzIHkNCjI6IHJlZmVycyB0byBub24tc3ludGFjdGljIG5hbWVzDQozOiBjb2x1bW5pemVkIGRhdGUgZW50cnkgdGhhdCBpcyBjdXN0b21pc2VkIGFuZCBsYXllZCBvdXQgdG8gYmUgZWFzaWVyIHRvIHJlYWQNCg0KIyMjIDEwLjM6IFRpYmJsZXMgdnMgZGF0YS5mcmFtZQ0KDQpFbnRlciB5b3VyIGNvZGUgY2h1bmtzIGZvciBTZWN0aW9uIDEwLjIgaGVyZS4NCg0KYGBge3J9DQp0aWJibGUoDQogIGEgPSBsdWJyaWRhdGU6Om5vdygpICsgcnVuaWYoMWUzKSAqIDg2NDAwLA0KICBiID0gbHVicmlkYXRlOjp0b2RheSgpICsgcnVuaWYoMWUzKSAqIDMwLA0KICBjID0gMToxZTMsDQogIGQgPSBydW5pZigxZTMpLA0KICBlID0gc2FtcGxlKGxldHRlcnMsIDFlMywgcmVwbGFjZSA9IFRSVUUpDQopDQpgYGANCg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmZsaWdodHMgJT4lIA0KICBwcmludChuID0gMTAsIHdpZHRoID0gSW5mKQ0KYGBgDQoNCmBgYHtyfQ0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSANCiAgVmlldygpDQpgYGANCg0KYGBge3J9DQpkZiA8LSB0aWJibGUoDQogIHggPSBydW5pZig1KSwNCiAgeSA9IHJub3JtKDUpDQopDQoNCmRmJHgNCg0KZGZbWyJ4Il1dDQoNCmRmW1sxXV0NCmBgYA0KDQpgYGB7cn0NCmRmICU+JSAgLiR4DQpkZiAlPiUgIC5bWyJ4Il1dDQpgYGANCg0KRGVzY3JpYmUgd2hhdCBlYWNoIGNodW5rIGNvZGUgZG9lcy4gDQoxOnNob3dzIGZpcnN0IDEwIHJvd3MgYW5kIGFsbCBjb2x1bW5zIHRoYXQgZml0IG9uIHNjcmVlbiwgZWFzaWVyIHRvIHdvcmsgd2l0aCBsYXJnZSBkYXRhIGZyYW1lcw0KMjpjYW4gcHJpbnQgYSBzcGVjaWZpYyBkYXRhIGZyYW1lDQozOnNjcm9sbGFibGUgdmlldyBvZiB0aGUgZGF0YSBmcmFtZQ0KNDpwdWxscyBvdXQgYSBzaW5nbGUgdmFpcmFibGUgDQo1OnRvIHVzZSB2YXJpYWJsZXMgaW4gYSBwaXBlDQoNCiMjIyAxMC40OiBOb3QgcmVxdWlyZWQNCg0KIyMjIyBTZWN0aW9uIDEwLjUgUXVlc3Rpb25zDQoNCkFuc3dlciB0aGUgcXVlc3Rpb25zICpjb21wbGV0ZWx5LiogVXNlIGNvZGUgY2h1bmtzLCB0ZXh0LCBvciBib3RoLCBhcyBuZWNlc3NhcnkuDQoNCioqMToqKiBIb3cgY2FuIHlvdSB0ZWxsIGlmIGFuIG9iamVjdCBpcyBhIHRpYmJsZT8gKEhpbnQ6IHRyeSBwcmludGluZyBgbXRjYXJzYCwgd2hpY2ggaXMgYSByZWd1bGFyIGRhdGEgZnJhbWUpLiBJZGVudGlmeSBhdCBsZWFzdCB0d28gd2F5cyB0byB0ZWxsIGlmIGFuIG9iamVjdCBpcyBhIHRpYmJsZS4gKkhpbnQ6KiBXaGF0IGRvZXMgYGFzX3RpYmJsZSgpYCBkbz8gV2hhdCBkb2VzIGBjbGFzcygpYCBkbz8gV2hhdCBkb2VzIGBzdHIoKWAgZG8/DQoNCmBgYHtyfQ0KbXRjYXJzDQphc190aWJibGUobXRjYXJzKQ0KaXNfdGliYmxlKG10Y2FycykNCmNsYXNzKG10Y2FycykNCnN0cihtdGNhcnMpDQoNCmBgYA0KDQpVc2luZyBpc190aWJibGUgZm9yIHRydWUgb3IgZmFsc2UNCmFzX3RpYmJsZSBwcmludHMgb2ZmIHRoZSBmaXJzdCB0ZW4gb2JzZXJ2YXRpb25zDQpjbGFzcyBpcyB1c2VkIHRvIGZpbmQgdGhlIGNsYXNzIG9mIGFuIG9iamVjdA0Kc3RyIGdpdmVzIGEgc3VtbWFyeSBvZiB0aGUgdGFibGUNCg0KKioyOioqIENvbXBhcmUgYW5kIGNvbnRyYXN0IHRoZSBmb2xsb3dpbmcgb3BlcmF0aW9ucyBvbiBhIGRhdGEuZnJhbWUgYW5kIGVxdWl2YWxlbnQgdGliYmxlLiBXaGF0IGlzIGRpZmZlcmVudD8gV2h5IG1pZ2h0IHRoZSBkZWZhdWx0IGRhdGEgZnJhbWUgYmVoYXZpb3VycyBjYXVzZSB5b3UgZnJ1c3RyYXRpb24/DQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZShhYmMgPSAxLCB4eXogPSAiYSIpDQpkZiR4DQpkZlssICJ4eXoiXQ0KZGZbLCBjKCJhYmMiLCAieHl6IildDQpgYGANCg0KYGBge3J9DQp0YmwgPC0gYXNfdGliYmxlKGRmKQ0KdGJsJHgNCnRibFssICJ4eXoiXQ0KdGJsWywgYygiYWJjIiwgInh5eiIpXQ0KDQpgYGANCg0KdGhlIGRpZmZlcmVuY2UgaXMgaWYgdGhlIGRhdGEgZnJhbWVzIGhhdmUgdmFyaWFibGVzIHRoZW4gaXQgd2lsbCBjYXVzZSBhIHByb2JsZW0gaW4gdGhlIGNvZGUNCg0KDQojIyBDaGFwdGVyIDExOiBJbXBvcnRpbmcgZGF0YQ0KDQpSZWFkIFtSNGRzIENoYXB0ZXIgMTE6IERhdGEgSW1wb3J0XShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtaW1wb3J0Lmh0bWwpLCBzZWN0aW9ucyAxLCAyLCBhbmQgNS4NCg0KIyMjIDExLjEgSW50cm9kdWN0aW9uDQoNCk5vdGhpbmcgdG8gZG8gaGVyZSB1bmxlc3MgeW91IHRvb2sgYSBicmVhayBhbmQgbmVlZCB0byByZWxvYWQgYHRpZHl2ZXJzZWAuDQoNCiMjIyAxMS4yIEdldHRpbmcgc3RhcnRlZC4NCg0KRG8gKm5vdCogcnVuIHRoZSBmaXJzdCBjb2RlIGNodW5rIG9mIHRoaXMgc2VjdGlvbiwgd2hpY2ggYmVnaW5zIHdpdGggYGhlaWdodHMgPC0gcmVhZF9jc3YoImRhdGEvaGVpZ2h0cy5jc3YiKWAuIFlvdSBkbyBub3QgaGF2ZSB0aGF0IGRhdGEgZmlsZSBzbyB0aGUgY29kZSB3aWxsIG5vdCBydW4uDQoNCkVudGVyIGFuZCBydW4gdGhlIHJlbWFpbmluZyBjaHVua3MgaW4gdGhpcyBzZWN0aW9uLg0KDQpgYGB7cn0NCnJlYWRfY3N2KCJhLGIsYw0KICAgICAgICAgMSwyLDMNCiAgICAgICAgIDQsNSw2IikNCmBgYA0KDQpgYGB7cn0NCnJlYWRfY3N2KCJUaGUgZmlyc3QgbGluZSBvZiBtZXRhZGF0YQ0KICAgICAgICAgVGhlIHNlY29uZCBsaW5lIG9mIG1ldGFkYXRhDQogICAgICAgICB4LHkseg0KICAgICAgICAgMSwyLDMiLCBza2lwID0gMikNCg0KcmVhZF9jc3YoIiMgQSBjb21tZW50IEkgd2FudCB0byBza2lwDQogICAgICAgICB4LHkseg0KICAgICAgICAgMSwyLDMiLCBjb21tZW50ID0gIiMiKQ0KYGBgDQoNCmBgYHtyfQ0KcmVhZF9jc3YoIjEsMiwzXG40LDUsNiIsIGNvbF9uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KcmVhZF9jc3YoIjEsMiwzXG40LDUsNiIsIGNvbF9uYW1lcyA9IGMoIngiLCAieSIsICJ6IikpDQpgYGANCg0KYGBge3J9DQpyZWFkX2NzdigiYSxiLGNcbjEsMiwuIiwgbmEgPSAiLiIpDQpgYGANCg0KDQoNCiMjIyMgMTEuMiBRdWVzdGlvbnMNCg0KKioxOioqIFdoYXQgZnVuY3Rpb24gd291bGQgeW91IHVzZSB0byByZWFkIGEgZmlsZSB3aGVyZSBmaWVsZHMgd2VyZSBzZXBhcmF0ZWQgd2l0aCAifCI/DQoNCnJlYWRfZGVsaW0oPHRoZSBmaWxlPiAsIGRlbGltID0gInwiKQ0KDQoqKjI6KiogKFRoaXMgcXVlc3Rpb24gaXMgbW9kaWZpZWQgZnJvbSB0aGUgdGV4dC4pIEZpbmlzaCB0aGUgdHdvIGxpbmVzIG9mIGByZWFkX2RlbGltYCBjb2RlIHNvIHRoYXQgdGhlIGZpcnN0IG9uZSB3b3VsZCByZWFkIGEgY29tbWEtc2VwYXJhdGVkIGZpbGUgYW5kIHRoZSBzZWNvbmQgd291bGQgcmVhZCBhIHRhYi1zZXBhcmF0ZWQgZmlsZS4gWW91IG9ubHkgbmVlZCB0byB3b3JyeSBhYm91dCB0aGUgZGVsaW1pdGVyLiBEbyBub3Qgd29ycnkgYWJvdXQgb3RoZXIgYXJndW1lbnRzLiBSZXBsYWNlIHRoZSBkb3RzIGluIGVhY2ggbGluZSB3aXRoIHRoZSByZXN0IG9mIHlvdXIgY29kZS4gDQoNCiMgQ29tbWEtc2VwYXJhdGVkDQpgZmlsZSA8LSByZWFkX2RlbGltKCJmaWxlX2NzdiIsICIxLDIsMyIpYA0KDQojIFRhYi1zZXBhcmF0ZWQNCmBmaWxlIDwtIHJlYWRfZGVsaW0oImZpbGVfdHN2IiwgIjEsMiwzIilgDQoNCg0KKiozOioqIFdoYXQgYXJlIHRoZSB0d28gbW9zdCBpbXBvcnRhbnQgYXJndW1lbnRzIHRvIGByZWFkX2Z3ZigpYD8gV2h5Pw0KZndmX3Bvc2l0aW9ucyBhbmQgZndmX3dpZHRocw0Kc3BlY2lmaXkgYnkgcG9zaXRpb25zIG9yIHdpZHRocyBvZiBmaXhlZCB3aWR0aCBmaWxlcw0KDQoqKjQ6KiogU2tpcCB0aGlzIHF1ZXN0aW9uDQoNCg0KKio1OiAqKiBJZGVudGlmeSB3aGF0IGlzIHdyb25nIHdpdGggZWFjaCBvZiB0aGUgZm9sbG93aW5nIGlubGluZSBDU1YgZmlsZXMuIFdoYXQgaGFwcGVucyB3aGVuIHlvdSBydW4gdGhlIGNvZGU/DQoNCmBgYHtyfQ0KcmVhZF9jc3YoImEsYlxuMSwyLDNcbjQsNSw2IikNCnJlYWRfY3N2KCJhLGIsY1xuMSwyXG4xLDIsMyw0IikNCnJlYWRfY3N2KCJhLGJcblwiMSIpDQpyZWFkX2NzdigiYSxiXG4xLDJcbmEsYiIpDQpyZWFkX2NzdigiYTtiXG4xOzMiKQ0KYGBgDQoxOiBudW1iZXIgb2YgY29sdW1ucyBkb2VzbnQgbWF0Y2ggaGVhZGVyDQoyOiBudW1iZXIgb2YgY29sdW1ucyBkb2VzbnQgbWF0Y2ggaGVhZGVyDQozOiBjb2x1bW4gYiBpcyBOQQ0KNDogYSBiIGFyZSBpbiB0aGUgdmFsdWVzDQo1OiBzZW1pY29sb24gdXNlZCBpbnN0ZWFkIG9mIGNvbW1hDQoNCiMjIyAxMS4zIGFuZCAxMS40OiBOb3QgcmVxdWlyZWQNCg0KIyMjIDExLjU6IFdyaXRpbmcgdG8gYSBmaWxlDQoNCkp1c3QgcmVhZCB0aGlzIHNlY3Rpb24uIFlvdSBtYXkgZmluZCBpdCBoZWxwZnVsIGluIHRoZSBmdXR1cmUgdG8gc2F2ZSBhIGRhdGEgZmlsZSB0byB5b3VyIGhhcmQgZHJpdmUuIEl0IGlzIGJhc2ljYWxseSB0aGUgc2FtZSBmb3JtYXQgYXMgcmVhZGluZyBhIGZpbGUsIGV4Y2VwdCB0aGF0IHlvdSBtdXN0IHNwZWNpZnkgdGhlIGRhdGEgb2JqZWN0IHRvIHNhdmUsIGluIGFkZGl0aW9uIHRvIHRoZSBwYXRoIGFuZCBmaWxlIG5hbWUuDQoNCiMjIyAxMS42IE5vdCByZXF1aXJlZA0KDQojIyBDaGFwdGVyIDE4OiBQaXBlcw0KDQpSZWFkIFtSNGRzIENoYXB0ZXIgMTg6IFBpcGVzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3BpcGVzLmh0bWwpLCBzZWN0aW9ucyAxLTMuDQoNCk5vdGhpbmcgdG8gZG8gb3RoZXJ3aXNlIGZvciB0aGlzIGNoYXB0ZXIuIElzIHRoaXMgZWFzeSBvciB3aGF0Pw0KDQoqKk5vdGU6KiogVHJ5aW5nIHVzaW5nIHBpcGVzIGZvciBhbGwgb2YgdGhlIHJlbWFpbmluZyBleGFtcGxlcy4gVGhhdCB3aWxsIGhlbHAgeW91IHVuZGVyc3RhbmQgdGhlbS4NCg0KIyMgQ2hhcHRlciAxMjogVGlkeSBEYXRhDQoNClJlYWQgW1I0ZHMgQ2hhcHRlciAxMjogVGlkeSBEYXRhXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpZHktZGF0YS5odG1sKSwgc2VjdGlvbnMgMS0zLCA3LiANCg0KIyMjIDEyLjEgSW50cm9kdWN0aW9uDQoNCk5vdGhpbmcgdG8gZG8gaGVyZSB1bmxlc3MgeW91IHRvb2sgYSBicmVhayBhbmQgbmVlZCB0byByZWxvYWQgdGhlIGB0aWR5dmVyc2UuYA0KDQojIyMgMTIuMiBUaWR5IGRhdGENCg0KU3R1ZHkgRmlndXJlIDEyLjEgYW5kIHJlbGF0ZSB0aGUgZGlhZ3JhbSB0byB0aGUgdGhyZWUgcnVsZXMgbGlzdGVkIGp1c3QgYWJvdmUgdGhlbS4gUmVsYXRlIHRoYXQgYmFjayB0byB0aGUgZXhhbXBsZSBJIGdhdmUgeW91IGluIHRoZSBub3Rlcy4gQmVhciB0aGlzIGluIG1pbmQgYXMgeW91IG1ha2UgZGF0YSB0aWR5IGluIHRoZSBzZWNvbmQgcGFydCBvZiB0aGlzIGFzc2lnbm1lbnQuDQoNCllvdSBkbyBub3QgaGF2ZSB0byBydW4gYW55IG9mIHRoZSBleGFtcGxlcyBpbiB0aGlzIHNlY3Rpb24uDQoNCiMjIyAxMi4zDQoNClJlYWQgYW5kIHJ1biB0aGUgZXhhbXBsZXMgdGhyb3VnaCBzZWN0aW9uIDEyLjMuMSAoZ2F0aGVyaW5nKSwgaW5jbHVkaW5nIHRoZSBleGFtcGxlIHdpdGggYGxlZnRfam9pbigpYC4gV2UnbGwgY292ZXIgam9pbnMgbGF0ZXIuDQoNCmBgYHtyfQ0KdGFibGU0YQ0KdGlkeTRhIDwtIHRhYmxlNGEgJT4lIA0KICBnYXRoZXIoYDE5OTlgLGAyMDAwYCwga2V5ID0gInllYXIiLCB2YWx1ZSA9ICJjYXNlcyIpDQp0aWR5NGIgPC0gdGFibGU0YiAlPiUgDQogIGdhdGhlcihgMTk5OWAsYDIwMDBgLCBrZXkgPSAieWVhciIsIHZhbHVlID0gcG9wdWxhdGlvbikNCmxlZnRfam9pbih0aWR5NGEsIHRpZHk0YikNCg0KYGBgDQoNCg0KIyMjIyAxMi4zIFF1ZXN0aW9ucw0KDQoqKjI6KiogV2h5IGRvZXMgdGhpcyBjb2RlIGZhaWw/IEZpeCBpdCBzbyBpdCB3b3Jrcy4NCg0KYGBge3J9DQp0YWJsZTRhICU+JSANCiAgZ2F0aGVyKGAxOTk5YCwgYDIwMDBgLCBrZXkgPSAieWVhciIsIHZhbHVlID0gImNhc2VzIikNCiM+IEVycm9yIGluIGluZHNfY29tYmluZSgudmFycywgaW5kX2xpc3QpOiBQb3NpdGlvbiBtdXN0IGJlIGJldHdlZW4gMCBhbmQgbg0KYGBgDQp0aGUgeWVhcnMgZGlkIG5vdCBoYXZlIGJhY2sgdGlja21hcmtzIG9uIHRoZW0gYCANCg0KVGhhdCBpcyBhbGwgZm9yIENoYXB0ZXIgMTIuIE9uIHRvIHRoZSBsYXN0IGNoYXB0ZXIuDQoNCg0KIyMgQ2hhcHRlciA1OiBEYXRhIHRyYW5zZm9ybWF0aW9uDQoNClJlYWQgW1I0ZHMgQ2hhcHRlciA1OiBEYXRhIFRyYW5zZm9ybWF0aW9uXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RyYW5zZm9ybS5odG1sKSwgc2VjdGlvbnMgMS00Lg0KDQpUaW1lIHRvIFtnZXQgc21hbGwuXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUdPcmR6Q0hucHc0KSANCg0KIyMjIDUuMTogSW50cm9kdWN0aW9uDQoNCkxvYWQgdGhlIG5lY2Vzc2FyeSBsaWJyYXJpZXMuIEFzIHVzdWFsLCB0eXBlIHRoZSBleGFtcGxlcyBpbnRvIGFuZCBydW4gdGhlIGNvZGUgY2h1bmtzLg0KDQpgYGB7cn0NCm55Y2ZsaWdodHMxMzo6ZmxpZ2h0cw0KYGBgDQoNCg0KDQojIyMgNS4yOiBGaWx0ZXIgcm93cyB3aXRoIGBmaWx0ZXIoKWANCg0KU3R1ZHkgRmlndXJlIDUuMSBjYXJlZnVsbHkuIE9uY2UgeW91IGxlYXJuIHRoZSBgJmAsIGB8YCwgYW5kIGAhYCBsb2dpYywgeW91IHdpbGwgZmluZCB0aGVtIHRvIGJlIHZlcnkgcG93ZXJmdWwgdG9vbHMuDQoNCg0KIyMjIyA1LjIgUXVlc3Rpb25zDQoNCioqMS4xOioqIEZpbmQgYWxsIGZsaWdodHMgd2l0aCBhIGRlbGF5IG9mIDIgaG91cnMgb3IgbW9yZS4NCg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmZsaWdodHMNCmZsaWdodHMgPC0gbnljZmxpZ2h0czEzOjpmbGlnaHRzDQpmaWx0ZXIoZmxpZ2h0cyAsIGFycl9kZWxheSA+PSAxMjApDQpgYGANCg0KDQoqKjEuMjoqKiBGbGV3IHRvIEhvdXN0b24gKElBSCBvciBIT1UpDQoNCmBgYHtyfQ0KZmlsdGVyKGZsaWdodHMsIGRlc3QgPT0gIklBSCIgfCBkZXN0ID09ICJIT1UiKQ0KYGBgDQoNCg0KKioxLjM6KiogV2VyZSBvcGVyYXRlZCBieSBVbml0ZWQgKFVBKSwgQW1lcmljYW4gKEFBKSwgb3IgRGVsdGEgKERMKS4NCg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmFpcmxpbmVzDQpmaWx0ZXIoZmxpZ2h0cywgY2FycmllciAlaW4lIGMoIkFBIiwgIkRMIiwgIlVBIikpDQpgYGANCg0KDQoqKjEuNDoqKiBEZXBhcnRlZCBpbiBzdW1tZXIgKEp1bHksIEF1Z3VzdCwgYW5kIFNlcHRlbWJlcikuDQoNCmBgYHtyfQ0KZmlsdGVyKGZsaWdodHMsIG1vbnRoID49IDcsIG1vbnRoIDw9IDkpDQpgYGANCg0KDQoqKjEuNToqKiBBcnJpdmVkIG1vcmUgdGhhbiB0d28gaG91cnMgbGF0ZSwgYnV0IGRpZG7igJl0IGxlYXZlIGxhdGUuDQoNCmBgYHtyfQ0KZmlsdGVyKGZsaWdodHMsIGRlcF9kZWxheSA8PSAwLCBhcnJfZGVsYXkgPiAxMjApDQpgYGANCg0KKioxLjY6KiogV2VyZSBkZWxheWVkIGJ5IGF0IGxlYXN0IGFuIGhvdXIsIGJ1dCBtYWRlIHVwIG92ZXIgMzAgbWludXRlcyBpbiBmbGlnaHQuIFRoaXMgaXMgYSB0cmlja3kgb25lLiBEbyB5b3VyIGJlc3QuDQoNCmBgYHtyfQ0KZmlsdGVyKGZsaWdodHMsIGRlcF9kZWxheSA+PSA2MCwgZGVwX2RlbGF5IC0gYXJyX2RlbGF5ID4gMzApDQpgYGANCg0KDQoqKjEuNzoqKiBEZXBhcnRlZCBiZXR3ZWVuIG1pZG5pZ2h0IGFuZCA2YW0gKGluY2x1c2l2ZSkNCg0KYGBge3J9DQpmaWx0ZXIoZmxpZ2h0cywgZGVwX3RpbWUgPD0gNjAwIHwgZGVwX3RpbWUgPT0gMjQwMCkNCmBgYA0KDQoNCioqMjoqKiBBbm90aGVyIHVzZWZ1bCBkcGx5ciBmaWx0ZXJpbmcgaGVscGVyIGlzIGBiZXR3ZWVuKClgLiBXaGF0IGRvZXMgaXQgZG8/IENhbiB5b3UgdXNlIGl0IHRvIHNpbXBsaWZ5IHRoZSBjb2RlIG5lZWRlZCB0byBhbnN3ZXIgdGhlIHByZXZpb3VzIGNoYWxsZW5nZXM/DQoNCmZpbmRzIHRoZSBtb250aHMgaW5iZXR3ZWVuIDcmOQ0KdGhlIHN1bW1lciBtb250aHMgDQpgYGB7cn0NCmZpbHRlcihmbGlnaHRzLCBiZXR3ZWVuKG1vbnRoLCA3LCA5KSkNCmBgYA0KDQoNCioqMzoqKiBIb3cgbWFueSBmbGlnaHRzIGhhdmUgYSBtaXNzaW5nIGRlcF90aW1lPyBXaGF0IG90aGVyIHZhcmlhYmxlcyBhcmUgbWlzc2luZz8gV2hhdCBtaWdodCB0aGVzZSByb3dzIHJlcHJlc2VudD8NCg0KYGBge3J9DQpmaWx0ZXIoZmxpZ2h0cywgaXMubmEoZGVwX3RpbWUpKQ0KYGBgDQphcnJpdmFsIHRpbWUsIGRlcGFydHVyZSBkZWxheSwgYXJyaXZhbCBkZWxheQ0KY2FuY2VsZWQgZmxpZ2h0cyANCg0KKio0OioqIFdoeSBpcyBgTkEgXiAwYCBub3QgbWlzc2luZz8gV2h5IGlzIGBOQSB8IFRSVUVgIG5vdCBtaXNzaW5nPyBXaHkgaXMgYEZBTFNFICYgTkFgIG5vdCBtaXNzaW5nPyBDYW4geW91IGZpZ3VyZSBvdXQgdGhlIGdlbmVyYWwgcnVsZT8gKGBOQSAqIDBgIGlzIGEgdHJpY2t5IGNvdW50ZXJleGFtcGxlISkNCg0KYGBge3J9DQpOQSBeIDANCk5BIHwgVFJVRQ0KTkEgJiBGQUxTRQ0KYGBgDQpOQSBeIDAgaXMgbm90ID0gMA0KKipOb3RlOioqIEZvciBzb21lIGNvbnRleHQsIHNlZSBbdGhpcyB0aHJlYWRdKGh0dHBzOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDE2LzA3L3VuZGVyc3RhbmRpbmctbmEtaW4tci5odG1sKQ0KDQoNCiMjIyA1LjMgQXJyYW5nZSB3aXRoIGBhcnJhbmdlKClgDQoNCg0KIyMjIyA1LjMgUXVlc3Rpb25zDQoNCioqMToqKiBIb3cgY291bGQgeW91IHVzZSBgYXJyYW5nZSgpYCB0byBzb3J0IGFsbCBtaXNzaW5nIHZhbHVlcyB0byB0aGUgc3RhcnQ/IChIaW50OiB1c2UgaXMubmEoKSkuICoqTm90ZToqKiBUaGlzIG9uZSBzaG91bGQgc3RpbGwgaGF2ZSB0aGUgZWFybGllc3QgZGVwYXJ0dXJlIGRhdGVzIGFmdGVyIHRoZSBgTkFgcy4gKkhpbnQ6KiBXaGF0IGRvZXMgYGRlc2MoKWAgZG8/DQpwbGFjZXMgaXQgaW4gYXNjZW5kaW5nIG9yZGVyIA0KYGBge3J9DQphcnJhbmdlKGZsaWdodHMsIGRlc2MoaXMubmEoZGVwX3RpbWUpKSwgZGVwX3RpbWUpDQpgYGANCg0KKioyOioqIFNvcnQgZmxpZ2h0cyB0byBmaW5kIHRoZSBtb3N0IGRlbGF5ZWQgZmxpZ2h0cy4gRmluZCB0aGUgZmxpZ2h0cyB0aGF0IGxlZnQgZWFybGllc3QuIA0KDQpUaGlzIHF1ZXN0aW9uIGlzIGFza2luZyBmb3IgdGhlIGZsaWdodHMgdGhhdCB3ZXJlIG1vc3QgZGVsYXllZCAobGVmdCBsYXRlc3QgYWZ0ZXIgc2NoZWR1bGVkIGRlcGFydHVyZSB0aW1lKSBhbmQgbGVhc3QgZGVsYXllZCAobGVmdCBhaGVhZCBvZiBzY2hlZHVsZWQgdGltZSkuDQoNCm1vc3QgZGVsYXk6IGphbiA5IDIwMTMgYXQgOTowMA0KZWFybGllc3QgZGVwOiBkZWMgNyAyMDEzIGF0IDIxOjIzDQoNCmBgYHtyfQ0KYXJyYW5nZShmbGlnaHRzLCBkZXNjKGRlcF9kZWxheSkpDQphcnJhbmdlKGZsaWdodHMsIGRlcF9kZWxheSkNCmBgYA0KDQoqKjM6KiogU29ydCBmbGlnaHRzIHRvIGZpbmQgdGhlIGZhc3Rlc3QgZmxpZ2h0cy4gSW50ZXJwcmV0IGZhc3Rlc3QgdG8gbWVhbiBzaG9ydGVzdCB0aW1lIGluIHRoZSBhaXIuDQoNCmBgYHtyfQ0KYXJyYW5nZShmbGlnaHRzLCBhaXJfdGltZSkgDQpgYGANCg0KDQoqT3B0aW9uYWwgY2hhbGxlbmdlOiogZmFzdGVzdCBmbGlnaHQgY291bGQgcmVmZXIgdG8gZmFzdGVzdCBhaXIgc3BlZWQuIFNwZWVkIGlzIG1lYXN1cmVkIGluIG1pbGVzIHBlciBob3VyIGJ1dCB0aW1lIGlzIG1pbnV0ZXMuIEFycmFuZ2UgdGhlIGRhdGEgYnkgZmFzdGVzdCBhaXIgc3BlZWQuDQoNCmBgYHtyfQ0KYXJyYW5nZShmbGlnaHRzLCBkaXN0YW5jZSAvIGFpcl90aW1lICogNjApDQpgYGANCg0KDQoqKjQ6KiogV2hpY2ggZmxpZ2h0cyB0cmF2ZWxsZWQgdGhlIGxvbmdlc3Q/IFdoaWNoIHRyYXZlbGxlZCB0aGUgc2hvcnRlc3Q/DQoNCkxvbmdlc3Q6NDk4Mw0KU2hvcnRldDoxNw0KDQpgYGB7cn0NCmFycmFuZ2UoZmxpZ2h0cywgZGVzYyhkaXN0YW5jZSkpDQphcnJhbmdlKGZsaWdodHMsIGRpc3RhbmNlKQ0KYGBgDQoNCg0KIyMjIDUuNCBTZWxlY3QgY29sdW1ucyB3aXRoIGBzZWxlY3QoKWANCg0KIyMjIyA1LjQgUXVlc3Rpb25zDQoNCioqMToqKiBCcmFpbnN0b3JtIGFzIG1hbnkgd2F5cyBhcyBwb3NzaWJsZSB0byBzZWxlY3QgYGRlcF90aW1lYCwgYGRlcF9kZWxheWAsIGBhcnJfdGltZWAsIGFuZCBgYXJyX2RlbGF5YCBmcm9tIGZsaWdodHMuIEZpbmQgYXQgbGVhc3QgdGhyZWUgd2F5cy4NCg0KYGBge3J9DQpzZWxlY3QoZmxpZ2h0cywgZGVwX3RpbWUsIGRlcF9kZWxheSwgYXJyX3RpbWUsIGFycl9kZWxheSkNCnNlbGVjdChmbGlnaHRzLCA0LCA2LCA3LCA5KQ0Kc2VsZWN0KGZsaWdodHMsICJkZXBfdGltZSIsICJkZXBfZGVsYXkiLCAiYXJyX3RpbWUiLCAiYXJyX2RlbGF5IikNCmBgYA0KDQoNCioqMjoqKiBXaGF0IGhhcHBlbnMgaWYgeW91IGluY2x1ZGUgdGhlIG5hbWUgb2YgYSB2YXJpYWJsZSBtdWx0aXBsZSB0aW1lcyBpbiBhIGBzZWxlY3QoKWAgY2FsbD8NCg0KSWYgeW91IHJlcGVhdCB2YXJpYWJsZXMgdGhleSBnZXQgaWdub3JlZA0KDQoqKjM6KiogV2hhdCBkb2VzIHRoZSBgb25lX29mKClgIGZ1bmN0aW9uIGRvPyBXaHkgbWlnaHQgaXQgYmUgaGVscGZ1bCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoaXMgdmVjdG9yPw0KDQp2YXJzIDwtIGMoInllYXIiLCAibW9udGgiLCAiZGF5IiwgImRlcF9kZWxheSIsICJhcnJfZGVsYXkiKWANCg0KaXQgaXMgZWFzaWVyIHRvIHVzZSB2ZWN0b3JzIHRoYW4gIiINCg0KDQoqKjQ6KiogRG9lcyB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgdGhlIGZvbGxvd2luZyBjb2RlIHN1cnByaXNlIHlvdT8gSG93IGRvIHRoZSBzZWxlY3QgaGVscGVycyBkZWFsIHdpdGggY2FzZSBieSBkZWZhdWx0PyBIb3cgY2FuIHlvdSBjaGFuZ2UgdGhhdCBkZWZhdWx0Pw0KDQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiKSkNCmBgYHtyfQ0Kc2VsZWN0KGZsaWdodHMsIGNvbnRhaW5zKCJUSU1FIikpDQoNCmBgYA0KDQpgYGB7cn0NCnNlbGVjdChmbGlnaHRzLCBjb250YWlucygiVGltZSIsIGlnbm9yZS5jYXNlID0gRkFMU0UpKQ0KYGBgDQoNCg0K